package com.zhuiyun.project.config;

import com.zhuiyun.project.security.commonhandler.*;
import com.zhuiyun.project.security.customizelogin.usernamepasswordcode.UsernamePasswordCodeAuthenticationProvider;
import com.zhuiyun.project.security.customizelogin.usernamepasswordcode.UsernamePasswordCodeFilter;
import com.zhuiyun.project.security.customizelogin.wx.WechatAuthenticationProvider;
import com.zhuiyun.project.security.customizelogin.wx.WechatFilter;
import com.zhuiyun.project.security.jwt.ExceptionHandlerFilter;
import com.zhuiyun.project.security.jwt.JWTAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;

import java.util.Arrays;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * @ClassName WebSecurityConfig
 * @Description Security 权限配置
 * @Author zcy
 * @Date 2023/3/27 10:28
 **/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 自定登录失败做出的返回结果
     */
    @Autowired
    AuthenticationEntryPoint authExceptionEntryPoint;

    /**
     * 权限不足败做出的返回结果
     */
    @Autowired
    CustomAccessDeniedHandler customAccessDeniedHandler;

    /**
     * 自定义登出处理逻辑
     */
    @Autowired
    LogoutSuccessHandler logoutSuccessHandler;
    /**
     * 自定义用户名密码验证码信息认证
     */
    @Resource
    UsernamePasswordCodeAuthenticationProvider usernamePasswordCodeAuthenticationProvider;

    /**
     * 登录失败处理逻辑
     */
    @Autowired
    SysUserLoginAuthenticationFailureHandler sysUserLoginAuthenticationFailureHandler;

    /**
     * 自定义用户登录成功操作
     */
    @Resource
    SysUserLoginAuthenticationSuccessHandler sysUserLoginAuthenticationSuccessHandler;

    /**
     * 自定义wx认证
     */
    @Resource
    WechatAuthenticationProvider wechatAuthenticationProvider;
    /**
     * 登录失败处理逻辑
     */
    @Autowired
    WechatAuthenticationFailureHandler wechatAuthenticationFailureHandler;

    /**
     * 微信用户登录成功操作
     */
    @Resource
    WechatAuthenticationSuccessHandler wechatAuthenticationSuccessHandler;

    /**
     * 捕获token异常
     */
    @Autowired
    ExceptionHandlerFilter exceptionHandlerFilter;

    /**
     * 声明密码加密方式
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 未登录放行路径
     */
    private static final String[] AUTH_WHITELIST = {
            // -- swagger ui
            "/swagger-resources/**",
            "/swagger-ui.html",
            "/swagger-ui/",
            "/v3/api-docs",
            "/v2/api-docs",
            "/swagger-ui/**",
            "/webjars/**",
            "/images/**",
            "/js/**",
            "/sysUser/**",
            "/webjars/**",
            "/druid/**",
            "/sysuser/code",
            //微信小程序登录相关
            "/wxLogin/**",
            "/Sys/Article",
            "/Sys/ArticleMain",
            "/Sys/CommentAll",
            "/Sys/ReportType",
            "/Sys/ArticleLater",
            "/dictData/selectDict",
            "/user/getAvatarId",
            "/sysMenu/**",
            "/file/File",
            "/userPass/**",
            "/patient/**",
            "/disease/**",
            "/physicianPass/**",
//            "/file/getVideoById",
//            "/articleReview/**",
            "/publishArticle/publishArticle",
            "/userRules/**",
            "/file/isFile",
            "friend/updateMeted",
            "/report/userReportsy",
            "/group/Group",
            "/Sys/loginOut",
    };

    /**
     * 后台用户登录放行路径
     */
    private static final String[] SYS_WHITELIST = {
            "/sysMenu/**",
            "/sysUser/**",
            "/user/**",
            "/cancel/**",
            "/report/**",
            "/articleReview/**",
            "/Sys/ReportArticle",
            "/Sys/SysArticle",
            "/disease/getDiseaseById",
            "/group/createGroup",
            "/report/userReportsy",
            "/Sys/loginOut",
    };
    /**
     * 前端用户登录放行路径
     */
    private static final String[] APP_WHITELIST = {
            "/msgList/getMsgList",
            "/addFriend/getFriendRequestList",
            "/Sys/addComment",
            "/Sys/addReport",
            "/addFriend/addFriend",
            "/addFriend/getAddByUserIdAndFriendId",
            "/addFriend/addFriendOK",
            "/addFriend/addFriendNo",
            "/friend/getFriendByNameLike",
            "/friend/getFriendByFriendUserId",
            "/person/getChatRecord",
            "/person/cleanUnReadMsg",
            "/message/message",
            "/addFriend/queryAddFriendRecord",
            "/msgList/getMsgListByIds",
            "/report/userReportsy",

    };
    /**
     * 前台医师登录放行路径
     */
    private static final String[] PHYSICIAN_WHITELIST = {
            "/publishArticle/publishArticle",
            "/Sys/ArticleDoctorList",
            "/Sys/ArticleMain",
            "/Sys/delArticle",
            "/Sys/updatePrivacy",
            "/report/userReportsy",
    };

    /**
     * @param webSecurity
     * @Author kdj
     * @Description 忽略websocket拦截
     * @Date 2023/7/12 9:20
     * @Return void
     */
    @Override
    public void configure(WebSecurity webSecurity) {
        webSecurity.ignoring().antMatchers(
                "/dchat/**"
        );
    }
    /**
     * 注册 Security相关自定义过滤器
     *
     * @param http
     * @return
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests((auth) -> {
                            try {
                                auth.antMatchers(AUTH_WHITELIST).permitAll()
                                        //后台用户
                                        .antMatchers(SYS_WHITELIST).hasAnyAuthority("sys") //后台用户
                                        //前台用户
                                        .antMatchers(APP_WHITELIST).hasAnyAuthority("app")
                                        //医师用户
                                        .antMatchers(PHYSICIAN_WHITELIST).hasAnyAuthority("physician")
                                        //用户登录之后
                                        .anyRequest().authenticated()
                                        //异常处理(权限拒绝、登录失效等)
                                        .and().exceptionHandling().
                                        authenticationEntryPoint(authExceptionEntryPoint)//匿名用户访问无权限资源时的异常处理
                                        .accessDeniedHandler(customAccessDeniedHandler)//权限不够的异常处理
                                        // 登出
                                        .and().logout().
                                        permitAll().
                                        logoutSuccessHandler(logoutSuccessHandler) //登出成功处理逻辑
                                        .and().sessionManagement().
                                        sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                                        .and().cors().configurationSource(configurationSource()); // 允许跨域;//禁用session
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }
                )
                .httpBasic(withDefaults());
        // 禁用缓存
        http.headers().cacheControl();
        // 添加JWT过滤器
        http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager(http)));
        // 注册异常捕获过滤器
        http.addFilterBefore(exceptionHandlerFilter, CorsFilter.class);
        // 注册用户名密码验证码登录
        http.addFilterAt(usernamePasswordCodeFilter(http), UsernamePasswordAuthenticationFilter.class);
        // 微信小程序登录
        http.addFilterAt(wechatFilter(http), UsernamePasswordAuthenticationFilter.class);
    }


    /**
     * 注册自定义登录
     *
     * @param httpSecurity
     * @return
     * @throws Exception
     */
    @Bean("authenticationManager")
    @Primary
    AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .build();
        return authenticationManager;
    }

    @Bean("authenticationManagerCode")
    AuthenticationManager authenticationManagerCode(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(usernamePasswordCodeAuthenticationProvider)
                .build();
        return authenticationManager;
    }


    /**
     * 注册wx登录
     *
     * @param httpSecurity
     * @return
     * @throws Exception
     */
    @Bean("authenticationWx")
    AuthenticationManager authenticationWx(HttpSecurity httpSecurity) throws Exception {
        AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(wechatAuthenticationProvider)
                .build();
        return authenticationManager;
    }

    /**
     * 注册自定义用户 密码 验证码 登录拦截器 后台
     *
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public UsernamePasswordCodeFilter usernamePasswordCodeFilter(HttpSecurity http) throws Exception {
        UsernamePasswordCodeFilter usernamePasswordCodeFilter = new UsernamePasswordCodeFilter("/sysUser/login");
        usernamePasswordCodeFilter.setAuthenticationSuccessHandler(sysUserLoginAuthenticationSuccessHandler);
        usernamePasswordCodeFilter.setAuthenticationFailureHandler(sysUserLoginAuthenticationFailureHandler);
        usernamePasswordCodeFilter.setAuthenticationManager(authenticationManagerCode(http));
        return usernamePasswordCodeFilter;
    }

    /**
     * 注册自定义wx登录后台
     *
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public WechatFilter wechatFilter(HttpSecurity http) throws Exception {
        WechatFilter wechatFilter = new WechatFilter("/wxLogin/login");
        wechatFilter.setAuthenticationManager(authenticationWx(http));
        wechatFilter.setAuthenticationSuccessHandler(wechatAuthenticationSuccessHandler);
        wechatFilter.setAuthenticationFailureHandler(wechatAuthenticationFailureHandler);
        return wechatFilter;
    }


    /**
     * @Description 允许跨域
     * @Author lzy
     * @Date 2023/4/8 11:31
     * @Return org.springframework.web.cors.CorsConfigurationSource
     */
    CorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }

}
